#pragma once
#include <iostream>
#include <optional>
#include <filesystem>

// QADDON BY ZERO123
namespace QAddon
{
	inline const std::string MANIFEST = "manifest.json";
	// 旧版MANIFEST项目结构
	inline const std::string PACK_MANIFEST = "pack_manifest.json";
	// 一个基础MANIFEST清单结构
	inline const std::string BASE_MANIFEST = R"({"format_version":1,"header":{"description":"","min_engine_version":[1,18,0],"name":"","uuid":"{&UUID1}","version":[1,0,0]},"modules":[{"description":"","type":"{&TYPE}","uuid":"{&UUID2}","version":[1,0,0]}]})";
	inline const std::string BASE_BIND_MANIFEST = R"({"dependencies":[{"uuid":"{&A_UUID}","version":[1,0,0]}],"format_version":2,"header":{"description":"","min_engine_version":[1,18,0],"name":"","uuid":"{&H_UUID}","version":[1,0,0]},"modules":[{"description":"","type":"{&TYPE}","uuid":"{&UUID}","version":[1,0,0]}]})";

	enum PACK_TYPE
	{
		BEH_PACK,
		RES_PACL,
		UNKNOW,
	};

	// Addon包
	class Package
	{
		std::string dirPath;
		PACK_TYPE packType;
		std::string manifestFileName;
		std::string dirName;
	public:
		Package(const std::string& path, const std::string& manifestFileName, const std::string& dirName, PACK_TYPE packType = PACK_TYPE::UNKNOW):
			dirPath(path), manifestFileName(manifestFileName), dirName(dirName), packType(packType) {}

		const std::string& getDirName() const
		{
			return dirName;
		}

		const std::string& getDirPath() const
		{
			return dirPath;
		}

		const std::filesystem::path getManifestPath() const
		{
			return std::filesystem::path(dirPath) / manifestFileName;
		}

		PACK_TYPE getPackType() const
		{
			return packType;
		}

		const std::string& getManifestFileName() const
		{
			return manifestFileName;
		}
	};

	/// <summary>
	/// 从特定目录向下匹配Addon包
	/// </summary>
	/// <param name="fromPath">匹配目录</param>
	/// <returns>(行为包, 资源包) 排序的元组数据</returns>
	inline std::tuple<std::optional<Package>, std::optional<Package>> findAddonPackages(const std::filesystem::path& fromPath)
	{
		std::optional<Package> behPack = std::nullopt;
		std::optional<Package> resPack = std::nullopt;
		if (!std::filesystem::exists(fromPath))
		{
			// 若指定目录不存在则直接终止
			return std::make_tuple(behPack, resPack);
		}
		// 遍历当前目录
		for (const auto& entry : std::filesystem::directory_iterator(fromPath))
		{
			if (!entry.is_directory())
			{
				continue;
			}
			std::string matchManifest;
			const auto& path = entry.path();
			// 类网易 简易匹配算法
			if (std::filesystem::exists(path / MANIFEST))
			{
				matchManifest = MANIFEST;
			}
			else if (std::filesystem::exists(path / PACK_MANIFEST))
			{
				matchManifest = PACK_MANIFEST;
			}
			if (!matchManifest.empty())
			{
				// 找到Package包 匹配行为/资源包
				if (std::filesystem::exists(path / "entities"))
				{
					behPack = Package(path.string(), matchManifest, path.filename().string(), PACK_TYPE::BEH_PACK);
				}
				else if (std::filesystem::exists(path / "textures"))
				{
					resPack = Package(path.string(), matchManifest, path.filename().string(), PACK_TYPE::RES_PACL);
				}
			}
			if (behPack && resPack)
			{
				// FIND ALL BACK
				break;
			}
		}
		return std::make_tuple(behPack, resPack);
	}

	// 生成随机字符（十六进制）
	inline char randomHex()
	{
		int value = rand() % 16;
		return (value < 10) ? ('0' + value) : ('A' + (value - 10));
	}

	// C风格生成 UUID 的函数
	inline void C_GenerateUUID(char* uuid)
	{
		static bool _init = false;
		if (!_init)
		{
			_init = true;	// 初始化种子
			srand((unsigned int)time(NULL));
		}
		const int UUID_LENGTH = 36; // UUID 长度（包含分隔符）
		const char* format = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; // UUID 模板
		for (int i = 0; i < UUID_LENGTH; ++i)
		{
			switch (format[i])
			{
			case 'x':
				uuid[i] = randomHex();
				break;
			case '4':
				uuid[i] = '4'; // 固定版本号为 4
				break;
			case 'y':
				uuid[i] = "89AB"[rand() % 4]; // 固定 variant 为 8, 9, A 或 B
				break;
			case '-':
				uuid[i] = '-'; // 分隔符
				break;
			default:
				break;
			}
		}

		uuid[UUID_LENGTH] = '\0'; // 添加字符串结束符
	}

	// CPP风格生成UUID
	inline std::string generateUUID()
	{
		char uuid[37];
		C_GenerateUUID(uuid);
		return std::string(uuid);
	}

	// 字符串引用表替换函数 {&xxx} -> replaceStr
	inline std::string optimizedReplace(const std::string& templateStr, const std::unordered_map<std::string, std::string>& replacements, const std::string& unknow="null")
	{
		std::string result;
		result.reserve(templateStr.size()); // 预留空间，减少内存分配
		size_t i = 0;
		while (i < templateStr.size())
		{
			// 查找占位符 "{&"
			if (templateStr[i] == '{' && i + 1 < templateStr.size() && templateStr[i + 1] == '&')
			{
				size_t end = templateStr.find('}', i);
				if (end != std::string::npos)
				{
					// 提取占位符的键名并替换
					std::string key = templateStr.substr(i + 2, end - i - 2);
					auto it = replacements.find(key);
					if (it != replacements.end())
					{
						// 替换内容
						result.append(it->second);
					}
					else
					{
						// 无匹配
						result.append(unknow);
					}
					// 跳过整个占位符
					i = end + 1;
					continue;
				}
			}
			// 非占位符部分，直接追加
			result.push_back(templateStr[i]);
			++i;
		}
		return result;
	}

	// 创建一个无关联的行为Manfiest文本
	inline std::string createBehManifest()
	{
		return optimizedReplace(BASE_MANIFEST, {
			{"UUID1", generateUUID()},
			{"UUID2", generateUUID()},
			{"TYPE", "data"},
		});
	}

	// 创建一个无关联的资源Manfiest文本
	inline std::string createResManifest()
	{
		return optimizedReplace(BASE_MANIFEST, {
			{"UUID1", generateUUID()},
			{"UUID2", generateUUID()},
			{"TYPE", "resources"},
		});
	}

	// 创建具有关联绑定的行为和资源包Manifest文本
	inline std::tuple<std::string, std::string> createBindManifest()
	{
		std::string behUUID = generateUUID();
		std::string resUUID = generateUUID();
		return {
		optimizedReplace(BASE_BIND_MANIFEST, {
			{"H_UUID", behUUID},
			{"A_UUID", resUUID},
			{"UUID", generateUUID()},
			{"TYPE", "data"},
		}), optimizedReplace(BASE_BIND_MANIFEST, {
			{"H_UUID", resUUID},
			{"A_UUID", behUUID},
			{"UUID", generateUUID()},
			{"TYPE", "resources"},
		}), };
	}

	// 创建随机PACKAGE NAME
	inline std::string createRandomPackageName()
	{
		auto uuid = generateUUID();
		// 查找需要替换的子字符串
		size_t startPos = uuid.find("-");
		while (startPos != std::string::npos)
		{
			// 使用replace函数进行替换
			uuid.replace(startPos, 1, "");
			startPos = uuid.find("-");
		}
		return uuid;
	}
}
